
#include <string.h>
#include <openssl/evp.h>
//#include <mysql/mysql.h>
#include <mariadb/mysql.h>
#include "ccas/ccas.h"
#include "web.h"

int Query_from_User(struct DatabaseKeygen *dk, char *username, char *hash) {


	// 初始化 MySQL 连接句柄
	MYSQL *conn = mysql_init(NULL);
	if (conn == NULL) {
		fprintf(stderr, "mysql_init() failed\n");
		return -1;
	}

	// 连接到数据库
	if (mysql_real_connect(conn, dk->hostname, dk->username, dk->password, dk->database, 0, NULL, 0) == NULL) {
		fprintf(stderr, "mysql_real_connect() failed\n");
		mysql_close(conn);
		return -1;
	}

	// 由于主键即使插入失败也会自增，采用事务回滚的方式试图取消这一机制，失败

	// 初始化语句句柄
	MYSQL_STMT *stmt = mysql_stmt_init(conn);
	MYSQL_BIND bind[2];
	if (stmt == NULL) {
		fprintf(stderr, "mysql_stmt_init() failed\n");
		mysql_close(conn);
		return -1;
	}

	// 准备 SQL 语句，使用 ? 作为参数占位符
	string s = ccas_rf("sql/select_id_from_User.sql");
	char *stmt_str = ccas_ws(s);

	// 准备 SQL 语句
	if (mysql_stmt_prepare(stmt, stmt_str, strlen(stmt_str)) != 0) {

		fprintf(stderr, "mysql_stmt_prepare() failed, Error: %s\n", mysql_stmt_error(stmt));

		ccas_free(&s);
		free(stmt_str);

		mysql_stmt_close(stmt);
		mysql_close(conn);

		return -1;

	}
	ccas_free(&s);
	free(stmt_str);

	// 设置绑定结构体
	memset(bind, 0, sizeof(bind));
	printf("%s\n", username);

	bind[0].buffer_type = MYSQL_TYPE_VAR_STRING;
	bind[0].buffer = username;
	bind[0].buffer_length = strlen(username);
	bind[0].is_null = 0;
	bind[0].length = NULL;

	bind[1].buffer_type = MYSQL_TYPE_VAR_STRING;
	bind[1].buffer = hash;
	bind[1].buffer_length = strlen(hash);
	bind[1].is_null = 0;
	bind[1].length = NULL;



	// 绑定参数，这里我们绑定两个字符串类型的参数
	if (mysql_stmt_bind_param(stmt, bind) != 0) {
		fprintf(stderr, "mysql_stmt_bind_param() failed. Error: %s\n", mysql_stmt_error(stmt));
		mysql_stmt_close(stmt);
		mysql_close(conn);
		return -1;
	}

	// 执行语句
	if (mysql_stmt_execute(stmt) != 0) {
		fprintf(stderr, "mysql_stmt_execute() failed. Error: %s\n", mysql_stmt_error(stmt));
		//if (mysql_rollback(conn) != 0) {
		//	fprintf(stderr, "Rollback failed: %s\n", mysql_error(conn));
		//}
		mysql_stmt_close(stmt);
		mysql_close(conn);
		return -1;
	}


	int id = -1;
	unsigned long rlen = 0;
	my_bool is_null;
	my_bool error;

	MYSQL_BIND rbind[2];
	memset(rbind, 0, sizeof(rbind));

	rbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
	rbind[0].buffer = (char*)&id;
	rbind[0].buffer_length = sizeof(id);
	rbind[0].is_null = &is_null;
	rbind[0].length = &rlen;
	rbind[0].error = &error;

	if (mysql_stmt_bind_result(stmt, rbind) != 0) {
		fprintf(stderr, "mysql_stmt_bind_result() failed: %s\n", mysql_stmt_error(stmt));
		mysql_stmt_close(stmt);
		mysql_close(conn);
		return -1;
	}

	// Store the result set
	if (mysql_stmt_store_result(stmt) != 0) {
		fprintf(stderr, "mysql_stmt_store_result() failed: %s\n", mysql_stmt_error(stmt));
		mysql_stmt_close(stmt);
		mysql_close(conn);
		return -1;
	}

	int err;
        while ((err = mysql_stmt_fetch(stmt)) == 0) {
		if (!is_null) {
			printf("Fetched id: %d\n", id);
		} else {
			printf("Fetched id is NULL\n");
		}
	}

	if (err == MYSQL_NO_DATA) {
		printf("No data found.\n");
	} else if (err != 0) {
		fprintf(stderr, "mysql_stmt_fetch() failed: %s\n", mysql_stmt_error(stmt));	
	}


	unsigned int num_cols = mysql_stmt_field_count(stmt);
	printf("Number of columns: %u\n", num_cols);

	


	// 释放结果集资源
	mysql_stmt_free_result(stmt);

	// 关闭语句句柄
	mysql_stmt_close(stmt);

	// 关闭连接并结束库使用
	mysql_close(conn);

	return id;
}

/* 注意，插入失败后可能应该回滚，未写 */
int Insert_to_User(struct DatabaseKeygen *dk, char *username, char *hash) {

	// 初始化 MySQL 连接句柄
	MYSQL *conn = mysql_init(NULL);
	if (conn == NULL) {
		fprintf(stderr, "mysql_init() failed\n");
		return 1;
	}

	// 连接到数据库
	if (mysql_real_connect(conn, dk->hostname, dk->username, dk->password, dk->database, 0, NULL, 0) == NULL) {
		fprintf(stderr, "mysql_real_connect() failed\n");
		mysql_close(conn);
		return 1;
	}

	// 由于主键即使插入失败也会自增，采用事务回滚的方式试图取消这一机制，失败

	// 初始化语句句柄
	MYSQL_STMT *stmt = mysql_stmt_init(conn);
	MYSQL_BIND bind[2];
	if (stmt == NULL) {
		fprintf(stderr, "mysql_stmt_init() failed\n");
		mysql_close(conn);
		return 1;
	}

	// 准备 SQL 语句，使用 ? 作为参数占位符
	string s = ccas_rf("sql/insert_to_User.sql");
	char *stmt_str = ccas_ws(s);

	// 准备 SQL 语句
	if (mysql_stmt_prepare(stmt, stmt_str, strlen(stmt_str)) != 0) {

		fprintf(stderr, "mysql_stmt_prepare() failed, Error: %s\n", mysql_stmt_error(stmt));

		ccas_free(&s);
		free(stmt_str);

		mysql_stmt_close(stmt);
		mysql_close(conn);

		return 1;

	}
	ccas_free(&s);
	free(stmt_str);

	// 设置绑定结构体
	memset(bind, 0, sizeof(bind));

	bind[0].buffer_type = MYSQL_TYPE_VAR_STRING;
	bind[0].buffer = username;
	bind[0].buffer_length = strlen(username);
	bind[0].is_null = 0;
	bind[0].length = NULL;

	bind[1].buffer_type = MYSQL_TYPE_VAR_STRING;
	bind[1].buffer = hash;
	bind[1].buffer_length = strlen(hash);
	bind[1].is_null = 0;
	bind[1].length = NULL;



	// 绑定参数，这里我们绑定两个字符串类型的参数
	if (mysql_stmt_bind_param(stmt, bind) != 0) {
		fprintf(stderr, "mysql_stmt_bind_param() failed. Error: %s\n", mysql_stmt_error(stmt));
		mysql_stmt_close(stmt);
		mysql_close(conn);
		return 1;
	}

	// 执行语句
	if (mysql_stmt_execute(stmt) != 0) {
		fprintf(stderr, "mysql_stmt_execute() failed. Error: %s\n", mysql_stmt_error(stmt));
		//if (mysql_rollback(conn) != 0) {
		//	fprintf(stderr, "Rollback failed: %s\n", mysql_error(conn));
		//}
		mysql_stmt_close(stmt);
		mysql_close(conn);
		return 1;
	}

	// 清理语句句柄
	mysql_stmt_close(stmt);

	// 关闭连接
	mysql_close(conn);

	return 0;

}

void sha256(const char *input, char *output) {
	unsigned char hash[EVP_MAX_MD_SIZE];
	unsigned int hash_len;
	EVP_MD_CTX *ctx = EVP_MD_CTX_new();
	if (ctx == NULL) {
		perror("EVP_MD_CTX_new failed");
		return;
	}
	if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)) {
		perror("EVP_DigestInit_ex failed");
		EVP_MD_CTX_free(ctx);
		return;
	}
	if (!EVP_DigestUpdate(ctx, input, strlen(input))) {
		perror("EVP_DigestUpdate failed");
		EVP_MD_CTX_free(ctx);
		return;
	}
	if (!EVP_DigestFinal_ex(ctx, hash, &hash_len)) {
		perror("EVP_DigestFinal_ex failed");
		EVP_MD_CTX_free(ctx);
		return;
	}
	EVP_MD_CTX_free(ctx);

        //printf("%u\n", hash_len);
	for (int i = 0; i < hash_len; i++) {
		sprintf(output + (i * 2), "%02x", hash[i]);
	}
}
